package com.mfz.common.dbHelper

import org.apache.logging.log4j.LogManager
import java.sql.Connection
import java.sql.ResultSet

internal val log = LogManager.getLogger(Helper::class.java.name)
typealias CreateConnection = () -> Connection

/**
 * 数据库操作帮助类.
 *
 * 每个对象做为一个查询或修改使用。
 *
 * 当需要执行多次操作时,可持有此接口的实例,并且设置autoCommit为false.
 * 此后,必须在结束时手工调用commit或rollback方法。
 *
 */
interface Helper {
	var lastUseTime: Long
	val isInUse: Boolean

	/**
	 * 初始化对象的方法。
	 *
	 * 在使用过后也需要再调用当前的初始化方法来重用当前连接
	 */
	fun init()

	/**
	 * 此方法执行后Helper被标记为已使用
	 *
	 * isInThread:指明当前对象是放在一个线程当中使用
	 * 正常操作应不调用此方法
	 */
	fun use(isInThread: Boolean)

	/**
	 * 返回当前连接是否已经被关闭
	 *
	 */
	fun isClosed(): Boolean

	fun getAutoCommit(): Boolean
	/**
	 * 执行一条单独的查询语句，必须以select开头，并且将获得的第一行第一列的数据返回.
	 *
	 * @param sql 需要执行的sql语句
	 * @param then 成功后执行的操作
	 * @return 返回第一行第一列的值,无论获得多少.
	 */
	@Suppress("UNCHECKED_CAST")
	fun <T> queryWithOneValue(sql: String, then: (Any) -> T = { it as T }): T?

	/**
	 * 执行一个无返回值的SQL操作.
	 *
	 * @param sql 需要执行的sql语句
	 * @param then 成功后执行的操作
	 * @return 返回影响的行数
	 */
	fun execute(sql: String, then: (Int) -> Int = { it }): Int


	/**
	 * 执行查询语句，
	 *
	 * @param sql 需要执行的sql语句
	 * @param then 成功后执行的操作,query方法将只调用一次ResultSet.回调函数必需要再次写while循环方法
	 */
	fun <T> queryBySet(sql: String, then: (ResultSet) -> T?): T?

	/**
	 * 执行查询语句，
	 *
	 *  执行此操作必须先使用select()方法,设置了要查询的对象
	 * @param sql 需要执行的sql语句
	 * @param then 成功后执行的操作,query方法将循环ResultSet.回调函数不需要再次写while循环方法
	 */
	fun query(sql: String, then: (ResultSet) -> Unit)

	/**
	 * 开始一个事务.
	 *
	 * 此处事务将是单层的，即已经开始了一个事务，无法进行嵌套。
	 * 已经开始过的事务在调用此方法时将直接返回false
	 */
	fun beginTran()

	/**
	 * 提交数据操作
	 *
	 */
	fun commit()

	/**
	 * 回滚操作
	 *
	 */
	fun rollback()


	/**
	 * 根据数据库格式，格式相关数据。
	 */
	fun format(value: Any?): String

	/**
	 * 操作完成后调用此操作来结束此对象的使用.并将资源返回以后使用.
	 *
	 * 所有操作结束以后，必须调用此方法返还数据连接对象。
	 * 可使用线程的开始和结束时获取和返回连接
	 *
	 * 如果事务开启时直接调用此方法,将会执行rollback()方法后结束对象。
	 * 此方法在调用后再次使用此对象任何方法均将获得一个OperatorIsEnd的异常。
	 *
	 * saveData:指明如果有未提交的事务，是否在关闭前提交。默认false。
	 *
	 * closeThread: 指明是否关闭线程对象指定的连接对象。关闭的话，下次此线程可以再次请求得到新的连接对象
	 * commit:指明是回滚还是提交事务，默认提交事务
	 */
	fun close(needSaveData: Boolean = false, closeThread: Boolean = false, commit: Boolean = true)

	/**
	 * 给定一个表名,返回一个表结构对象
	 */
	fun getTableInfo(tableName: String): MutableList<Field>
}

@Suppress("unused")
enum class ColumnType(private val className: String) {
	UNKNOWN("Any"), INT4("Int"), INT8("Long"), INT2("Int"), JSON("JSONObject"), VARCHAR("String"), TIMESTAMP("Date"), MONEY("Double"), VARCHAR_ARRAY("StringArray"), INT4_ARRAY("IntArray"), NUMERIC("Double"), BIT("Byte"), BOOLEAN("Boolean"), UUID("String"), INT2_ARRAY("IntArray"), INT8_ARRAY("LongArray"), TEXT("String");

	fun getJavaClassName(): String {
		return className
	}


}

data class Field(val name: String, val type: ColumnType, val notNull: Boolean, val defaultValue: String, private val description: String, val isPrimary: Boolean, val isSequence: Boolean, val isView: Boolean = false) {
	override fun toString(): String {
		return "列名:$name\t\t类型:$type\t\t必填:$notNull\t\t默认值:$defaultValue\t\t描述:$description\t\t主键:$isPrimary\t\t序列:$isSequence"
	}
}

abstract class AbstractJDBCHelper(
		internal var next: Helper?, private var conn: Connection, private val pool: ConnectionPool, private val createConnection: CreateConnection) : Helper {

	/**
	 * 表明当前连接是否已经在一个事务内
	 */
	private var inTransaction: Boolean = false
	/**
	 * 指明此对象是否在连接内，如果非，在关闭时将会被立即关闭。
	 * 如果是，调用close() 方法时必须指定needSaveData值为true
	 */
	private var isInThread: Boolean = false
	override var lastUseTime: Long = System.currentTimeMillis()
	private var _isInUse = false

	override val isInUse: Boolean
		get() = _isInUse

	private fun checkOperator() {
		if (conn.isClosed) {
			throw OperatorIsEnd("此对象的操作已经过期,请重新申请对象。连接状态:${conn.isClosed}")
		}
	}

	init {
		init()
	}

	final override fun init() {
		if (inTransaction && !conn.autoCommit) {
			conn.rollback()
		}

		_isInUse = false
		conn.autoCommit = true
		inTransaction = false
		lastUseTime = System.currentTimeMillis()
	}

	override fun toString(): String {
		return "Helper对象：${super.toString()},Conn对象: $conn"
	}

	override fun getAutoCommit(): Boolean {
		return conn.autoCommit
	}

	override fun <T> queryWithOneValue(sql: String, then: (Any) -> T): T? {
		checkOperator()

		log.trace("执行sql语句:$sql")
		var result: T? = null
		try {
			conn.createStatement().use {
				it.executeQuery(sql).use { row ->
					result = if (row.next()) then(row.getObject(1)) else null
				}
			}
			close()
		} catch (e: Exception) {
			log.error("执行SQL语句出现异常:$sql", e)
			close()
			throw e
		}

		return result
	}

	override fun execute(sql: String, then: (Int) -> Int): Int {
		checkOperator()

		var result = -1
		log.trace("执行sql语句: $sql")

		try {
			conn.createStatement().use { result = then(it.executeUpdate(sql)) }
			close()
		} catch (e: Exception) {
			log.error("执行SQL语句出现异常:$sql ", e)
			close()
			throw e
		}

		return result
	}


	override fun <T> queryBySet(sql: String, then: (ResultSet) -> T?): T? {
		checkOperator()

		log.debug("sql:$sql")
		var result: T? = null
		try {
			conn.createStatement().use {
				it.executeQuery(sql).use { row ->
					result = then(row)
				}
			}
			close()
		} catch (e: Exception) {
			log.error("执行SQL语句出现异常:", e)
			close()
			throw e
		}

		return result
	}

	override fun query(sql: String, then: (ResultSet) -> Unit) {
		checkOperator()

		log.debug("sql:$sql")
		try {
			conn.createStatement().use {
				it.executeQuery(sql).use { row ->
					while (row.next()) {
						then(row)
					}
				}
			}
			close()
		} catch (e: Exception) {
			log.error("执行SQL语句出现异常:", e)
			close()
			throw e
		}
	}

	override fun beginTran() {
		checkOperator()

		//已经存在一个事务
		if (inTransaction) {
			if (!conn.autoCommit) {
				//当设置false,表示启动事务，当再次设置时报异常
				log.error("事务已经开始,不能再次开始")
				throw TransactionExists()
			} else {
				//清除事务前进行回滚
				log.warn("事务已经存在，但是对象当中未记录有事务，先回滚再说。")
				conn.rollback()
			}
		}

		conn.autoCommit = false
		inTransaction = true
	}

	override fun commit() {
		checkOperator()

		if (inTransaction) {
			conn.commit()
			inTransaction = false

			log.trace("事务正常结束")
		} else {
			log.warn("没有需要结束的事务")
		}
	}

	override fun rollback() {
		checkOperator()

		if (inTransaction) {
			conn.rollback()
			inTransaction = false

			log.trace("事务已经提交回滚")
		} else {
			log.warn("没有需要结束的事务")
		}
	}

	private fun realClose() {
		conn.close()
	}

	internal fun renewConnection() {
		log.trace("正在更换连接")
		realClose()
		this.conn = createConnection()
	}


	override fun close(needSaveData: Boolean, closeThread: Boolean, commit: Boolean) {
		//当对象在一个线程获取对象中
		if (isInThread && !closeThread) {
//			log.trace("当前在一个线程当中,不进行关闭操作")
			return
		}

		if (needSaveData && inTransaction) {
			log.trace("进行提交事务操作。${if(commit) "提交" else "回滚"}")
			//根据要求进行提交或者回滚
			if (commit)
				conn.commit()
			else conn.rollback()
		}

		synchronized(pool.lock) {
			this._isInUse = false
			conn.autoCommit = true

			//关闭当前连接对应的线程记录信息
			pool.clearThreadHelper(Thread.currentThread())

			pool.lock.notifyAll()
		}
	}

	override fun use(isInThread: Boolean) {
		if (this._isInUse) {
			throw ConnectionIsUsed()
		}

		synchronized(pool.lock) {
			this.init()
			this.isInThread = isInThread
			this._isInUse = true

			//使用在线程当中，立即开启事务
			if (isInThread) beginTran()
		}
	}


	override fun isClosed(): Boolean {
		return !_isInUse || conn.isClosed
	}
}

//TODO 这是其他连接库使用
//
//@Suppress("unused")
//class MSSQLServer(connection: Connection, next: Helper) : AbstractJDBCHelper(connection, next) {
//	override fun getTableInfo(tableName: String): MutableList<Field> {
//		TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
//	}
//
//	override fun format(value: Any?): String {
//		TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
//	}
//}
//
//@Suppress("unused")
//class MySql(connection: Connection, next: Helper) : AbstractJDBCHelper(connection, next) {
//	override fun getTableInfo(tableName: String): MutableList<Field> {
//		TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
//	}
//
//	override fun format(value: Any?): String {
//		TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
//	}
//}
//
//@Suppress("unused")
//class H2(connection: Connection, next: Helper) : AbstractJDBCHelper(connection, next) {
//
//
//	override fun getTableInfo(tableName: String): MutableList<Field> {
//		TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
//	}
//
//	override fun format(value: Any?): String {
//		TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
//	}
//}
//
//@Suppress("unused")
//class OracleHelper(connection: Connection, next: Helper) : AbstractJDBCHelper(connection, next) {
//	override fun getTableInfo(tableName: String): MutableList<Field> {
//		TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
//	}
//
//	override fun format(value: Any?): String {
//		TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
//	}
//}
